/**
 * @author toplabs
 * 圆圈连线动态背景
 */
// 最小和最大半径，半径阈值和实心圆的百分比
const radMin = 5;
const radMax = 125;
const filledCircle = 60; // 实心圆的百分比
const concentricCircle = 30; // 同心圆百分比
const radThreshold = 25; // IFF特殊，在此半径上同心，否则填充

// 最小和最大移动速度
const speedMin = 0.3;
const speedMax = 2.5;

// 每个圆的最大可达到不透明度和模糊效果
const maxOpacity = 0.6;

// 默认调色板选择
const colors = ['52,168,83', '117,95,147', '199,108,23', '194,62,55', '0,172,212', '120,120,120'];
const bgColors = ['52,168,83', '117,95,147', '199,108,23', '194,62,55', '0,172,212', '120,120,120'];
const circleBorder = 10;
const backgroundMlt = 0.85;
let canvas;

// 链接的最小距离
let linkDist;
const lineBorder = 2.5;

// 最重要的是：整体圆和包含它们的数组的数量
const maxCircles = 12;
const points = [];
const pointsBack = [];

// 实验变量
let circleExp = 1;
const circleExpMax = 1.003;
const circleExpMin = 0.997;
let circleExpSp = 0.00004;
const circlePulse = false;

// circle class
function Circle (background) {
    // 如果有背景，它有不同的规则
    this.background = (background || false);
    this.x = randRange(-canvas.width / 2, canvas.width / 2);
    this.y = randRange(-canvas.height / 2, canvas.height / 2);
    this.radius = background ? hyperRange(radMin, radMax) * backgroundMlt : hyperRange(radMin, radMax);
    this.filled = this.radius < radThreshold ? (randInt(0, 100) > filledCircle ? false : 'full') : (randInt(0, 100) > concentricCircle ? false : 'concentric');
    this.color = background ? bgColors[randInt(0, bgColors.length - 1)] : colors[randInt(0, colors.length - 1)];
    this.borderColor = background ? bgColors[randInt(0, bgColors.length - 1)] : colors[randInt(0, colors.length - 1)];
    this.opacity = 0.05;
    this.speed = (background ? randRange(speedMin, speedMax) / backgroundMlt : randRange(speedMin, speedMax)); // * (radMin / this.radius);
    this.speedAngle = Math.random() * 2 * Math.PI;
    this.speedx = Math.cos(this.speedAngle) * this.speed;
    this.speedy = Math.sin(this.speedAngle) * this.speed;
    const spacex = Math.abs((this.x - (this.speedx < 0 ? -1 : 1) * (canvas.width / 2 + this.radius)) / this.speedx);
    const spacey = Math.abs((this.y - (this.speedy < 0 ? -1 : 1) * (canvas.height / 2 + this.radius)) / this.speedy);
    this.ttl = Math.min(spacex, spacey);
}

Circle.prototype.init = function () {
    Circle.call(this, this.background);
}

// 支持功能
// 生成随机整数a <= x <= b
function randInt (a, b) {
    return Math.floor(Math.random() * (b - a + 1) + a);
}

// 产生随机浮动
function randRange (a, b) {
    return Math.random() * (b - a) + a;
}

// 产生随机浮动，更可能接近
function hyperRange (a, b) {
    return Math.random() * Math.random() * Math.random() * (b - a) + a;
}

// 渲染功能
function drawCircle (ctx, circle) {
    // circle.radius *= circleExp;
    const radius = circle.background ? circle.radius *= circleExp : circle.radius /= circleExp;
    ctx.beginPath();
    ctx.arc(circle.x, circle.y, radius * circleExp, 0, 2 * Math.PI, false);
    ctx.lineWidth = Math.max(1, circleBorder * (radMin - circle.radius) / (radMin - radMax));
    ctx.strokeStyle = ['rgba(', circle.borderColor, ',', circle.opacity, ')'].join('');
    if (circle.filled === 'full') {
        ctx.fillStyle = ['rgba(', circle.borderColor, ',', circle.background ? circle.opacity * 0.8 : circle.opacity, ')'].join('');
        ctx.fill();
        ctx.lineWidth = 0;
        ctx.strokeStyle = ['rgba(', circle.borderColor, ',', 0, ')'].join('');
    }
    ctx.stroke();
    if (circle.filled === 'concentric') {
        ctx.beginPath();
        ctx.arc(circle.x, circle.y, radius / 2, 0, 2 * Math.PI, false);
        ctx.lineWidth = Math.max(1, circleBorder * (radMin - circle.radius) / (radMin - radMax));
        ctx.strokeStyle = ['rgba(', circle.color, ',', circle.opacity, ')'].join('');
        ctx.stroke();
    }
    circle.x += circle.speedx;
    circle.y += circle.speedy;
    if (circle.opacity < (circle.background ? maxOpacity : 1)) circle.opacity += 0.01;
    circle.ttl--;
}

// 初始化功能
function init (element) {
    canvas = document.getElementById(element);
    canvas.height = window.innerHeight
    canvas.width = window.innerWidth
    // 链接的最小距离
    linkDist = Math.min(canvas.width, canvas.height) / 2.4;
    // 填充屏幕
    for (let i = 0; i < maxCircles * 2; i++) points.push(new Circle());
    for (let i = 0; i < maxCircles; i++) pointsBack.push(new Circle(true));
    window.requestAnimationFrame(draw);
}

// 渲染功能
function draw () {
    if (circlePulse) {
        if (circleExp < circleExpMin || circleExp > circleExpMax) circleExpSp *= -1;
        circleExp += circleExpSp;
    }
    const ctxfr = canvas.getContext('2d');

    ctxfr.globalCompositeOperation = 'destination-over';
    ctxfr.clearRect(0, 0, canvas.width, canvas.height); // clear canvas

    ctxfr.save();
    ctxfr.translate(canvas.width / 2, canvas.height / 2);

    // 用于渲染每个圆，其连接并管理其越界替换的功能
    function renderPoints (ctx, arr) {
        for (let i = 0; i < arr.length; i++) {
            const circle = arr[i];
            // 检查是否超出范围
            if (circle.ttl < -20) arr[i].init(arr[i].background);
            // if (Math.abs(circle.y) > yEscape || Math.abs(circle.x) > xEscape) arr[i].init(arr[i].background);
            drawCircle(ctx, circle);
        }
        for (let i = 0; i < arr.length - 1; i++) {
            for (let j = i + 1; j < arr.length; j++) {
                const deltaX = arr[i].x - arr[j].x;
                const deltaY = arr[i].y - arr[j].y;
                const dist = Math.pow(Math.pow(deltaX, 2) + Math.pow(deltaY, 2), 0.5);
                // 如果圆圈重叠，则没有激光将其连接
                if (dist <= arr[i].radius + arr[j].radius) continue;
                // 否则，仅当dist为<linkDist时，我们才连接它们
                if (dist < linkDist) {
                    const xi = (arr[i].x < arr[j].x ? 1 : -1) * Math.abs(arr[i].radius * deltaX / dist);
                    const yi = (arr[i].y < arr[j].y ? 1 : -1) * Math.abs(arr[i].radius * deltaY / dist);
                    const xj = (arr[i].x < arr[j].x ? -1 : 1) * Math.abs(arr[j].radius * deltaX / dist);
                    const yj = (arr[i].y < arr[j].y ? -1 : 1) * Math.abs(arr[j].radius * deltaY / dist);
                    ctx.beginPath();
                    ctx.moveTo(arr[i].x + xi, arr[i].y + yi);
                    ctx.lineTo(arr[j].x + xj, arr[j].y + yj);
                    ctx.strokeStyle = ['rgba(', arr[i].borderColor, ',', Math.min(arr[i].opacity, arr[j].opacity) * ((linkDist - dist) / linkDist), ')'].join('');
                    ctx.lineWidth = (arr[i].background ? lineBorder * backgroundMlt : lineBorder) * ((linkDist - dist) / linkDist); //* ((linkDist-dist)/linkDist);
                    ctx.stroke();
                }
            }
        }
    }

    renderPoints(ctxfr, points);
    ctxfr.restore();
    window.requestAnimationFrame(draw);
}
init('toplabs_circle_wrapper_canvas')